#include "Parser.h"

/* Parser */
void initParser(char ptr filename)
{
    initScanner(filename);
    initSymTabStack();
}

void primeval()
{
    mark("Primeval", 0);
    if(curKey-> sym neq ENDF)
    {
        get();
        while(curKey-> sym equ INVOL)
        {
            get();
            involvement();
        }
        sequences();
        statementSequence();
    }
    printf("Primeval done!");
}

void sequenceList()
{
    if(curKey-> sym equ IDENT)
    {
        get();
        while(curKey-> sym equ PERIOD)
        {
            get();
            sequenceList();
        }
    }
    else
    {
        mark("Identifier expected", 1);
    }
}

void statementSequence()
{
    while (curKey-> sym neq ENDF)
    {
        while(curKey-> sym neq IDENT and
          curKey-> sym neq TYP and curKey-> sym neq STRUCT and
          curKey-> sym neq FUNC and curKey-> sym neq CONST and
          curKey-> sym neq TYPEDEF and curKey-> sym neq ENDF)
        {
            get();
            mark("statement expected", 1);
        }

        if(curKey-> sym equ FUNC)
        {
            procedureDeclaration();
        }
        else
        {
            declaration();
        }
    }
    mark("AWESOME JOB, MAN!!!", 0);
}

void statement()
{
    if(curKey-> sym equ TYP or curKey-> sym equ STRUCT or curKey-> sym equ CONST)
    {
        declaration();
    }
    elif(curKey-> sym equ WHILE)
    {
        whileStatement();
    }
    elif(curKey-> sym equ IF)
    {
        ifStatement();
    }
    elif(curKey-> sym equ IDENT)
    {
        object_t o = search(getCurValue());
        if(o equ NULL)
        {
            mark("Unknown identifier", 1);
            get();
            return;
        }
        get();
        if(curKey-> sym equ LPAREN)
        {
            actualParameters();
        }
        elif(curKey-> sym equ BECOME)
        {
            get();
            expression();
        }
        elif(curKey-> sym equ ARROW or
             curKey-> sym equ LBRAK)
        {
            selector(o-> type);
            if(curKey-> sym equ BECOME)
            {
                get();
                expression();
            }
            else
            {
                mark("Assignment expected", 1);
            }
        }
        else
        {
            if(curKey-> sym equ COMMA)
            {
                identList();
            }
            else
            {
                identListDeclaration(o-> type, CLASSVAR);
            }
            if(curKey-> sym equ BECOME)
            {
                get();
                expression();
            }
        }
        if(curKey-> sym equ COLON)
        {
            get();
        }
        else
        {
            mark("Semicolon expected", 1);
        }
    }
    elif (curKey-> sym equ RETURN)
    {
        get();
        if(curKey-> sym equ COLON)
        {
            get();
        }
        else
        {
            expression();
            if(curKey-> sym equ COLON)
            {
                get();
            }
            else
            {
                mark("Semicolon expected at return Statement", 1);
            }
        }
    }
    else
    {
        mark("Statement expected", 1);
    }
    mark("Statement successfully parsed", 0);
}

void procedureDeclaration()
{
    if(curKey-> sym equ FUNC)
    {
        get();
        type();
        if(curKey-> sym equ IDENT)
        {
            get();
            if(curKey-> sym equ LPAREN)
            {
                get();
                if(curKey-> sym equ TYP or curKey-> sym equ CONST or
                   curKey-> sym equ STRUCT or curKey-> sym equ IDENT)
                {
                    formalParameters();
                }
                if(curKey-> sym equ RPAREN)
                {
                    get();
                    if(curKey-> sym equ COLON)
                    {
                        get();
                    }
                    elif(curKey-> sym equ LBRACE)
                    {
                        body();
                    }
                    else
                    {
                        mark("Semicolon or left brace expected", 1);
                    }
                }
                else
                {
                    mark("Right parentity expected", 1);
                }
            }
            else
            {
                mark("Left parentity expected", 1);
            }
        }
        else
        {
            mark("Identifier expected", 1);
        }
    }
    else
    {
        mark("void expected", 1);
    }
    mark("Procedure successfully parsed", 0);
}

void body()
{
    if(curKey-> sym equ LBRACE)
    {
        get();
        newStage();
        while(curKey-> sym equ IDENT or curKey-> sym equ IF or curKey-> sym equ WHILE or
              curKey-> sym equ TYP or curKey-> sym equ STRUCT or curKey-> sym equ CONST or
              curKey-> sym equ RETURN)
        {
            statement();
        }
        if(curKey-> sym equ RBRACE)
        {
            get();
        }
        else
        {
            mark("Right brace expected(body)", 1);
        }
        pop();
    }
    else
    {
        mark("Left brace expected", 1);
    }
    mark("Body successfully parsed", 0);
}

void formalParameters()
{
    type();
    if(curKey-> sym equ IDENT)
    {
        get();
    }
    else
    {
        mark("Identifier expected", 1);
    }
    while(curKey-> sym equ COMMA)
    {
        get();
        formalParameters();
    }
    mark("Formal parameters successfully parsed", 0);
}

void declaration()
{
    if(curKey-> sym equ STRUCT)
    {
        get();
        if(curKey-> sym equ IDENT)
        {
            string_t typeOrName = getCurValue();
            get();
            if(curKey-> sym equ PTR)
            {
                get();
                object_t typeObject = search(typeOrName);
                if(typeObject neq NULL)
                {
                    type_t curType = typeObject-> type;
                    while(curKey-> sym equ PTR)
                    {
                        get();
                        type_t tmp = newType;
                        tmp-> form = FORMARRAY;
                        tmp-> base = curType;
                        curType = tmp;
                    }
                    if(curKey-> sym equ IDENT)
                    {
                        identListDeclaration(curType, CLASSVAR);
                        if(curKey-> sym equ BECOME)
                        {
                            get();
                            expression();
                        }
                    }
                    else
                    {
                        mark("Identifier expected", 1);
                    }
                }
                else
                {
                    mark("Unknown identifier", 1);
                }
            }
            elif(curKey-> sym equ LBRACE)
            {
                get();
                type_t curType = newType;
                curType-> form = FORMSTRUCT;
                curType-> fields = newSymTab;
                initSubSymTab(curType-> fields);

                object_t o = newObject;
                o-> oClass = CLASSTYPE;
                o-> name = typeOrName;
                o-> type = curType;

                while(curKey-> sym equ TYP or curKey-> sym equ STRUCT or
                      curKey-> sym equ IDENT)
                {
                    fieldDeclaration(curType-> fields);
                }
                newObj(o);
                if(curKey-> sym equ RBRACE)
                {
                    get();
                }
                else
                {
                    mark("Right brace expected (struct type)", 1);
                }
            }
            else
            {
                mark("Pointer or Identifier expected", 1);
            }
        }
        else
        {
            mark("Identifier expected", 1);
        }
    }
    elif(curKey-> sym equ TYPEDEF)
    {
        get();
        typeDeclaration();
    }
    else
    {
        if(curKey-> sym equ CONST)
        {
            get();
        }
        type_t curType = type();
        if(curType equ NULL or curType equ voidType)
        {
            mark("Unknown Type", 1);
        }
        identListDeclaration(curType, CLASSVAR);
        if(curKey-> sym equ BECOME)
        {
            get();
            expression();
        }
    }
    if(curKey-> sym equ COLON)
    {
        get();
    }
    else
    {
        mark("Semicolon expected", 1);
    }
    mark("Declaration successfully parsed", 0);
}

void typeDeclaration()
{
    type_t curType = type();
    identListDeclaration(curType, CLASSTYPE);
}

void fieldDeclaration(symTab_t symTab)
{
    type_t curType = type();
    identListFields(curType, symTab);
    if(curKey-> sym equ COLON)
    {
        get();
    }
    else
    {
        mark("Semicolon expected", 1);
    }
    mark("FieldType successfully parsed", 0);
}

type_t type()
{
    type_t type = NULL;
    if(curKey-> sym equ TYP)
    {
        string_t val = getCurValue();
        if(strcmp(val, "int") equ 0)
        {
            type = intType;
        }
        elif(strcmp(val, "bool") equ 0)
        {
            type = boolType;
        }
        elif(strcmp(val, "char") equ 0)
        {
            type = charType;
        }
        elif(strcmp(val, "void") equ 0)
        {
            type = voidType;
        }
        else
        {
            mark("This kind of basic type is unknown", 1);
            return NULL;
        }
        get();
    }
    elif(curKey-> sym equ STRUCT)
    {
        get();
        type = structType();
    }
    elif(curKey-> sym equ IDENT)
    {
        object_t o = search(getCurValue());
        if(o neq NULL)
        {
            type = o-> type;
        }
        else
        {
            mark("Unknown identifier", 1);
        }
        get();
    }
    else
    {
        mark("Type or struct expected", 1);
    }
    while(curKey-> sym equ PTR)
    {
        type_t tmp = newType;
        tmp-> form = FORMARRAY;
        tmp-> base = type;
        type = tmp;
        get();
    }
    mark("Type successfully parsed", 0);

    return type;
}

type_t structType()
{
    type_t type = NULL;
    if(curKey-> sym equ IDENT)
    {
        object_t o = search(getCurValue());
        if(o neq NULL)
        {
            type = o-> type;
        }
        else
        {
            mark("Unknown identifier: ", 1);
        }
        get();
        if(curKey-> sym equ PTR)
        {
            get();
        }
        else
        {
            mark("Pointer expected", 1);
        }
    }
    else
    {
        mark("Identifier expected", 1);
    }
    return type;
}

void identListDeclaration(type_t type, int class)
{
    object_t o = newObject;
    o-> type = type;
    o-> oClass = class;
    if(curKey-> sym equ IDENT)
    {
        o-> name = getCurValue();
        newObj(o);
        get();
        if(curKey-> sym equ COMMA)
        {
            get();
            identListDeclaration(type, class);
        }
    }
    else
    {
        mark("Identifier expected identlist", 1);
    }
    mark("Identlist successfully parsed", 0);
}

void identListFields(type_t type, symTab_t symTab)
{
    object_t o = newObject;
    o-> type = type;
    o-> oClass = CLASSVAR;
    if(curKey-> sym equ IDENT)
    {
        o-> name = getCurValue();
        addObj(symTab, o);
        get();
        if(curKey-> sym equ COMMA)
        {
            get();
            identListFields(type, symTab);
        }
    }
    else
    {
        mark("Identifier expected identlist", 1);
    }
    mark("Identlist successfully parsed", 0);
}


void identList()
{
    if(curKey-> sym equ IDENT)
    {
        object_t o = search(getCurValue());
        if(o equ NULL)
        {
            mark("Unknown Symbol", 1);
        }
        get();
        if(curKey-> sym equ COMMA)
        {
            get();
            identList();
        }
    }
    else
    {
        mark("Identifier expected identlist", 1);
    }
    mark("Identlist successfully parsed", 0);
}

/*expression = simpleExpression [( "equ" | "neq" | "<" | ">" | "<=" | ">=") simpleExpression].*/
void expression()
{
    simpleExpression();
    while(curKey-> sym equ EQL or curKey-> sym equ NEQ or curKey-> sym equ LSS or
       curKey-> sym equ GRT or curKey-> sym equ LEQ or curKey-> sym equ GEQ)
    {
        get();
        simpleExpression();
    }
    mark("Expression successfully parsed", 0);
}

void simpleExpression()
{
    if(curKey-> sym equ PLUS or curKey-> sym equ MINUS)
    {
        get();
    }
    term();
    while(curKey-> sym equ PLUS or curKey-> sym equ MINUS or curKey-> sym equ OR)
    {
        get();
        term();
    }
    mark("Simple Expression successfully parsed", 0);
}

void term()
{
    factor();
    while(curKey-> sym equ MOD or curKey-> sym equ TIMES or curKey-> sym equ DIV or curKey-> sym equ AND)
    {
        get();
        factor();
    }
    mark("Term successfully Parsed", 0);
}

void factor()
{
    if(curKey-> sym equ IDENT)
    {
        object_t o = search(getCurValue());
        get();
        if(o neq NULL)
        {
            if(curKey-> sym equ LPAREN)
            {
                actualParameters();
            }
            selector(o-> type);
        }
        else
        {
            mark("Identifier unknown!", 1);
        }

    }
    elif(curKey-> sym equ NUMBER)
    {
        get();
    }
    elif(curKey-> sym equ CHAR)
    {
        get();
    }
    elif(curKey-> sym equ LPAREN)
    {
        get();
        expression();
        if(curKey-> sym equ RPAREN)
        {
            get();
        }
        else
        {
            mark("Right parentity expected", 1);
        }
    }
    elif(curKey-> sym equ STRING)
    {
        get();
    }
    elif(curKey-> sym equ STRUCT)
    {
        get();
        structType();
        while(curKey-> sym equ PTR)
        {
            get();
        }
    }
    elif(curKey-> sym equ TYP)
    {
        get();
        while(curKey-> sym equ PTR)
        {
            get();
        }
    }
    else
    {
        mark("Factor expected", 1);
    }
    mark("Factor successfully Parsed", 0);
}

void selector(type_t t)
{
    type_t curType = t;
    while(curKey-> sym equ LBRAK or curKey-> sym equ ARROW)
    {
        if(curKey-> sym equ ARROW)
        {
            get();
            if(curKey-> sym neq IDENT)
            {
                mark("Identifier expected(Selector)", 1);
            }
            else
            {
                if(curType-> form neq FORMSTRUCT)
                {
                    mark("You can not use select -> for this type", 1);
                }
                else
                {
                    object_t found = getByName(curType-> fields, getCurValue());
                    if(found equ NULL)
                    {
                        mark("Unknown Field", 1);
                    }
                    else
                    {
                        curType = found-> type;
                    }
                }
                get();
            }
        }
        elif(curKey-> sym equ LBRAK)
        {
            if(curType-> form neq FORMARRAY)
            {
                mark("You can not use selector [] for this type", 1);
            }
            else
            {
                curType = curType-> base;
            }
            get();
            expression();
            if(curKey-> sym neq RBRAK)
            {
                mark("Left bracket expected", 1);
            }
            else
            {
                get();
            }
        }
    }
    mark("Selector successfully parsed", 0);
}

void ifStatement()
{
    if(curKey-> sym equ IF)
    {
        get();
        if(curKey-> sym equ LPAREN)
        {
            get();
            expression();
            if(curKey-> sym equ RPAREN)
            {
                get();
                body();
                while(curKey-> sym equ ELIF)
                {
                    get();
                    if(curKey-> sym equ LPAREN)
                    {
                        get();
                        expression();
                        if(curKey-> sym equ RPAREN)
                        {
                            get();
                            body();
                        }
                        else
                        {
                            mark("Right parentity expected", 1);
                        }
                    }
                    else
                    {
                        mark("Left parentity expected", 1);
                    }
                }
                if(curKey-> sym equ ELSE)
                {
                    get();
                    body();
                }
            }
            else
            {
                mark("Right parentity expected", 1);
            }
        }
        else
        {
            mark("Left parentity expected", 1);
        }
    }
    else
    {
        mark("if expected", 1);
    }
    mark("If statement successfully parsed", 0);
}

void whileStatement()
{
    if(curKey-> sym equ WHILE)
    {
        get();
        if(curKey-> sym equ LPAREN)
        {
            get();
            expression();
            if(curKey-> sym equ RPAREN)
            {
                get();
                body();
            }
            else
            {
                mark("Right parentity expected", 1);
            }
        }
        else
        {
            mark("Left parentity expected", 1);
        }
    }
    else
    {
        mark("while expected", 1);
    }
    mark("While statement successfully parsed", 0);
}

void actualParameters()
{
    if(curKey-> sym equ LPAREN)
    {
        get();
        if(curKey-> sym equ RPAREN)
        {
            get();
        }
        else
        {
            expression();
            while(curKey-> sym equ COMMA)
            {
                get();
                expression();
            }
            if(curKey-> sym neq RPAREN)
            {
                mark("Right parentity expected", 1);
            }
            else
            {
                get();
            }
        }
    }
    mark("ActualParameters successfully parsed", 0);
}

void sequenceSelector()
{
    while(curKey-> sym equ PERIOD)
    {
        get();
        if(curKey-> sym equ IDENT)
        {
            get();
        }
        else
        {
            mark("Identifier expected", 1);

        }
    }
}

void sequences()
{
    if(curKey-> sym equ SEQ)
    {
        get();
        sequenceList();
        if(curKey-> sym equ COLON)
        {
            get();
        }
        else
        {
            mark("Semicolon expected", 1);
        }
    }
    else
    {
        mark("sequence expected", 1);
    }
    mark("Sequence successfully parsed", 0);
}

void involvement()
{
    if(curKey-> sym equ IDENT)
    {
        get();
        sequenceSelector();
        if(curKey-> sym equ COLON)
        {
            get();
        }
        else
        {
            mark("semicolon expected", 1);
        }
    }
    else
    {
        mark("identifier expected", 1);
    }
    mark("Involve successfully parsed", 0);
}

void clearParser()
{
    clearScanner();
}
